package com.moonciki.multids.util;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import java.beans.Introspector;
import java.util.List;
import java.util.Map;

/**
 * 管理bean类
 */
@Component
public class MultiSpringBeanUtil implements ApplicationContextAware {
    /** applicationContext **/
    private static ApplicationContext applicationContext;

    /** Spring Bean 工厂 **/
    private static DefaultListableBeanFactory beanFactory;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

        MultiSpringBeanUtil.applicationContext = applicationContext;
        beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
    public static DefaultListableBeanFactory getBeanFactory() {
        return beanFactory;
    }

    /**
     * 根据 class 生成 spring 默认的beanName
     * @param clazz
     * @return
     */
    public static String getDefaultBeanName(Class clazz){
        String shortClassName = ClassUtils.getShortName(clazz.getName());
        String beanName = Introspector.decapitalize(shortClassName);
        return beanName;
    }

    /**
     * 动态添加 spring bean
     * @param clazz 类
     * @param beanName bean名称，为空时使用默认规则生成
     * @param propertyMap 类内各参数名称与值,可以为空
     * @param destroyMethod 销毁方法，可以为空
     * @return
     */
    public static boolean addBean(Class clazz, String beanName, Map<String, Object> propertyMap, String destroyMethod){
        return addBean(clazz, beanName, propertyMap, null, null, destroyMethod);
    }

    /**
     * 动态增加 spring bean
     * @param clazz 类
     * @param beanName bean名称，为空时使用默认规则生成
     * @param propertyMap 类内各参数名称与值,可以为空
     * @param referenceMap 类内引用，可以为空
     * @param constructorArgs 构造函数，可以为空
     * @param destroyMethod 销毁方法，可以为空
     * @return
     */
    public static boolean addBean(Class clazz, String beanName, Map<String, Object> propertyMap, Map<String, String> referenceMap, List<Object> constructorArgs, String destroyMethod) {

        if(MultiCommonUtils.stringIsBlank(beanName)){
            beanName = getDefaultBeanName(clazz);
        }

        // 判断Spring容器中是否存在该Bean
        if (beanFactory.containsBeanDefinition(beanName)) {
            return true;
        }

        String className = clazz.getName();

        BeanDefinitionBuilder beanBuilder = BeanDefinitionBuilder.rootBeanDefinition(className);
        beanBuilder.getBeanDefinition().setAttribute("id", beanName);

        // 设置bean参数属性
        if (!MultiCommonUtils.mapIsEmpty(propertyMap)) {
            for(String oneKey : propertyMap.keySet()){
                Object oneValue = propertyMap.get(oneKey);

                beanBuilder.addPropertyValue(oneKey, oneValue);
            }
        }

        // 设置bean参数引用
        if (!MultiCommonUtils.mapIsEmpty(referenceMap)) {
            for(String oneKey : referenceMap.keySet()){

                String oneValue = referenceMap.get(oneKey);

                beanBuilder.addPropertyReference(oneKey, oneValue);
            }
        }

        // 设置bean构造参数属性
        if (!MultiCommonUtils.collectionIsEmpty(constructorArgs)) {
            for(Object oneObj : constructorArgs){
                beanBuilder.addConstructorArgValue(oneObj);
            }
        }

        if (!MultiCommonUtils.stringIsBlank(destroyMethod)) {
            beanBuilder.setDestroyMethodName(destroyMethod);
        }

        AbstractBeanDefinition definition = beanBuilder.getBeanDefinition();

        beanFactory.registerBeanDefinition(beanName, definition);

        return true;
    }

    /**
     * 获取Bean
     *
     * @param requiredType
     * @return
     */
    public static <T> T getBean(Class<T> requiredType) {
        return applicationContext.getBean(requiredType);
    }

    /**
     * 获取bean
     *
     * @param beanName
     * @param requiredType
     * @return
     */
    public static <T> T getBean(String beanName, Class<T> requiredType) {
        return applicationContext.getBean(beanName, requiredType);
    }

}
